/*-------------------------------------------------------------------------
 *
 * libpq-int.h
 *	  此文件包含仅供前端 libpq 库使用的内部定义，而不供调用它的应用程序使用。
 *
 *	  应用程序可以包含此文件，如果它想绕过 libpq-fe.h 定义的官方 API，但那段代码在 PostgreSQL 发布之间更容易出现问题，而不是仅使用官方 API 的代码。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/interfaces/libpq/libpq-int.h
 *
 *-------------------------------------------------------------------------
 */

#ifndef LIBPQ_INT_H
#define LIBPQ_INT_H

/* 我们假设已包含 libpq-fe.h。 */
#include "libpq-events.h"

#include <time.h>
#ifndef WIN32
#include <sys/time.h>
#endif

#ifdef ENABLE_THREAD_SAFETY
#ifdef WIN32
#include "pthread-win32.h"
#else
#include <pthread.h>
#endif
#include <signal.h>
#endif

/* 包含与前端和后端共同的内容 */
#include "getaddrinfo.h"
#include "libpq/pqcomm.h"
/* 包含仅在前端找到的内容 */
#include "fe-auth-sasl.h"
#include "pqexpbuffer.h"

#ifdef ENABLE_GSS
#if defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#else
#include <gssapi/gssapi.h>
#endif
#endif

#ifdef ENABLE_SSPI
#define SECURITY_WIN32
#if defined(WIN32) && !defined(_MSC_VER)
#include <ntsecapi.h>
#endif
#include <security.h>
#undef SECURITY_WIN32

#ifndef ENABLE_GSS
/*
 * 定义与 Unix 上的 GSSAPI 兼容的假结构。
 */
typedef struct
{
	void	   *value;
	int			length;
} gss_buffer_desc;
#endif
#endif							/* ENABLE_SSPI */

#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>

#ifndef OPENSSL_NO_ENGINE
#define USE_SSL_ENGINE
#endif
#endif							/* USE_OPENSSL */

/*
 * POSTGRES 后端依赖的常量。
 */
#define CMDSTATUS_LEN 64		/* 应与 COMPLETION_TAG_BUFSIZE 匹配 */

/*
 * PGresult 及其子类型 PGresAttDesc、PGresAttValue
 * 表示查询的结果（更确切地说，是单个 SQL
 * 命令 --- 提供给 PQexec 的查询字符串可以包含多个命令）。
 * 请注意，我们假设单个命令最多可以返回一个元组组，
 * 因此不需要多个描述符集。
 */

/* PGresult 的子存储管理结构。
 * 有关详细信息，请参见 fe-exec.c 中的空间管理例程。
 * 请注意，space[k] 指的是从物理
 * 块头部开始的第 k 个字节 --- 它是一个联合，不是一个结构！
 */
typedef union pgresult_data PGresult_data;

union pgresult_data
{
	PGresult_data *next;		/* 链接到下一个块，或 NULL */
	char		space[1];		/* 访问块作为字节的虚拟变量 */
};

/* 有关准备语句单个参数的数据 */
typedef struct pgresParamDesc
{
	Oid			typid;			/* 类型ID */
} PGresParamDesc;

/*
 * 单个元组的单个属性的数据
 *
 * 我们对属性值使用 char*。
 *
 * 值指针始终指向一个以 null 结尾的区域；我们在后端发送的
 * 内容后添加一个 null（零）字节。这对文本值特别有用……对于
 * 二进制值，值可能包含嵌入的 null，因此应用程序不能在其上
 * 使用 C 字符串操作符。但为了保持一致性，我们还是添加了 null。
 * 请注意，值本身不包含长度字。
 *
 * NULL 属性在两个方面是一个特例：其 len 字段为 NULL_LEN，
 * 其值字段指向拥有 PGresult 的 null_field。查询结果中的所有
 * NULL 属性都指向同一个地方（无需为每一个单独存储 null 字符串）。
 */

#define NULL_LEN		(-1)	/* NULL 值的 pg_result len */

typedef struct pgresAttValue
{
	int			len;			/* 值的字节长度 */
	char	   *value;			/* 实际值，加上终止的零字节 */
} PGresAttValue;

/* 消息字段列表条目的类型定义 */
typedef struct pgMessageField
{
	struct pgMessageField *next;	/* 列表链接 */
	char		code;			/* 字段代码 */
	char		contents[FLEXIBLE_ARRAY_MEMBER];	/* 值，以 nul 结束 */
} PGMessageField;

/* 处理通知所需的字段 */
typedef struct
{
	PQnoticeReceiver noticeRec; /* 通知消息接收者 */
	void	   *noticeRecArg;
	PQnoticeProcessor noticeProc;	/* 通知消息处理器 */
	void	   *noticeProcArg;
} PGNoticeHooks;

typedef struct PGEvent
{
	PGEventProc proc;			/* 在事件上调用的函数 */
	char	   *name;			/* 仅用于错误消息 */
	void	   *passThrough;	/* 在注册时提供的指针 */
	void	   *data;			/* 可选状态（实例）数据 */
	bool		resultInitialized;	/* 如果 RESULTCREATE/COPY 成功则为 T */
} PGEvent;

struct pg_result
{
	int			ntups;
	int			numAttributes;
	PGresAttDesc *attDescs;
	PGresAttValue **tuples;		/* 每个 PGresult 元组都是一个
								 * PGresAttValue 的数组 */
	int			tupArrSize;		/* 元组数组的分配大小 */
	int			numParameters;
	PGresParamDesc *paramDescs;
	ExecStatusType resultStatus;
	char		cmdStatus[CMDSTATUS_LEN];	/* 查询的命令状态 */
	int			binary;			/* 如果 binary == 1，则为二进制元组值，
								 * 否则为文本 */

	/*
	 * 这些字段是从源 PGconn 复制的，以便 PGresult
	 * 上的操作不必引用 PGconn。
	 */
	PGNoticeHooks noticeHooks;
	PGEvent    *events;
	int			nEvents;
	int			client_encoding;	/* 编码 ID */

	/*
	 * 错误信息（如果不是错误结果则全部为NULL）。errMsg是
	 * 由PQresultErrorMessage返回的“整体”错误消息。如果我们有逐字段信息，则存储在链表中。
	 */
	char	   *errMsg;			/* 错误消息，如果没有错误则为NULL */
	PGMessageField *errFields;	/* 消息分成字段 */
	char	   *errQuery;		/* 触发查询的文本，如果可用 */

	/* 查询结果中的所有NULL属性指向此空字符串 */
	char		null_field[1];

	/*
	 * 空间管理信息。注意，attDescs和错误信息如果不为NULL，则指向已分配的块。但是元组指向一个单独分配的块，以便我们可以重新分配它。
	 */
	PGresult_data *curBlock;	/* 最近分配的块 */
	int			curOffset;		/* 块中自由空间的起始偏移量 */
	int			spaceLeft;		/* 块中剩余的自由字节数 */

	size_t		memorySize;		/* 为此PGresult分配的总空间 */
};

/* PGAsyncStatusType定义查询执行状态机的状态 */
typedef enum
{
	PGASYNC_IDLE,				/* 什么都没有发生，兄弟 */
	PGASYNC_BUSY,				/* 查询正在进行中 */
	PGASYNC_READY,				/* 查询完成，等待客户端获取
								 * 结果 */
	PGASYNC_READY_MORE,			/* 查询完成，等待客户端获取
								 * 结果，期望从此查询中获得更多结果 */
	PGASYNC_COPY_IN,			/* 正在进行 Copy In 数据传输 */
	PGASYNC_COPY_OUT,			/* 正在进行 Copy Out 数据传输 */
	PGASYNC_COPY_BOTH,			/* 正在进行 Copy In/Out 数据传输 */
	PGASYNC_PIPELINE_IDLE,		/* 在管道模式中“闲置”在命令之间 */
} PGAsyncStatusType;

/* 目标服务器类型（target_session_attrs的解码值） */
typedef enum
{
	SERVER_TYPE_ANY = 0,		/* 任何服务器（默认） */
	SERVER_TYPE_READ_WRITE,		/* 读写服务器 */
	SERVER_TYPE_READ_ONLY,		/* 只读服务器 */
	SERVER_TYPE_PRIMARY,		/* 主服务器 */
	SERVER_TYPE_STANDBY,		/* 备用服务器 */
	SERVER_TYPE_PREFER_STANDBY, /* 优先选择备用服务器 */
	SERVER_TYPE_PREFER_STANDBY_PASS2	/* 第二次传递 - 表现与ANY相同 */
} PGTargetServerType;

/* 布尔值加上未知状态，对于我们可能需要获取的GUC */
typedef enum
{
	PG_BOOL_UNKNOWN = 0,		/* 当前未知 */
	PG_BOOL_YES,				/* 是（真） */
	PG_BOOL_NO					/* 否（假） */
} PGTernaryBool;

/* EnvironmentOptions[]数组的类型定义 */
typedef struct PQEnvironmentOption
{
	const char *envName,		/* 环境变量的名称 */
			   *pgName;			/* 相应SET变量的名称 */
} PQEnvironmentOption;

/* 参数状态列表条目的类型定义 */
typedef struct pgParameterStatus
{
	struct pgParameterStatus *next; /* 列表链接 */
	char	   *name;			/* 参数名称 */
	char	   *value;			/* 参数值 */
	/* 注意：名称和值存储在与结构相同的malloc块中。 */
} pgParameterStatus;

/* 大对象访问数据……仅在使用大对象代码时分配。 */
typedef struct pgLobjfuncs
{
	Oid			fn_lo_open;		/* 后端函数lo_open的OID		*/
	Oid			fn_lo_close;	/* 后端函数lo_close的OID		*/
	Oid			fn_lo_creat;	/* 后端函数lo_creat的OID		*/
	Oid			fn_lo_create;	/* 后端函数lo_create的OID	*/
	Oid			fn_lo_unlink;	/* 后端函数lo_unlink的OID	*/
	Oid			fn_lo_lseek;	/* 后端函数lo_lseek的OID		*/
	Oid			fn_lo_lseek64;	/* 后端函数lo_lseek64的OID	*/
	Oid			fn_lo_tell;		/* 后端函数lo_tell的OID		*/
	Oid			fn_lo_tell64;	/* 后端函数lo_tell64的OID	*/
	Oid			fn_lo_truncate; /* 后端函数lo_truncate的OID	*/
	Oid			fn_lo_truncate64;	/* 函数lo_truncate64的OID */
	Oid			fn_lo_read;		/* 后端函数LOread的OID		*/
	Oid			fn_lo_write;	/* 后端函数LOwrite的OID		*/
} PGlobjfuncs;

/* PGdataValue表示传递给行处理器的数据字段值。
 * 它可以是文本或二进制数据；文本数据不是以零结尾的。
 * SQL NULL由len < 0表示；然后值仍然有效，但
 * 没有数据字节。
 */
typedef struct pgDataValue
{
	int			len;			/* 数据长度（以字节为单位），如果为NULL则<0 */
	const char *value;			/* 数据值，不带零结尾 */
} PGdataValue;

/* 结构体pg_conn_host的主机地址类型枚举 */
typedef enum pg_conn_host_type
{
	CHT_HOST_NAME,
	CHT_HOST_ADDRESS,
	CHT_UNIX_SOCKET
} pg_conn_host_type;

/*
 * PGQueryClass跟踪每个命令队列条目或正在执行的特殊操作
 * 使用的查询协议
 */
typedef enum
{
	PGQUERY_SIMPLE,				/* 简单查询协议（PQexec） */
	PGQUERY_EXTENDED,			/* 完整扩展协议（PQexecParams） */
	PGQUERY_PREPARE,			/* 仅解析（PQprepare） */
	PGQUERY_DESCRIBE,			/* 描述语句或门户 */
	PGQUERY_SYNC,				/* 同步（在管道末尾） */
	PGQUERY_CLOSE
} PGQueryClass;

/*
 * 待处理命令队列中的一条目。
 */
typedef struct PGcmdQueueEntry
{
	PGQueryClass queryclass;	/* 查询类型 */
	char	   *query;			/* SQL命令，或如果没有/未知/OOM则为NULL */
	struct PGcmdQueueEntry *next;	/* 列表链接 */
} PGcmdQueueEntry;

/*
 * pg_conn_host存储与连接字符串中提到的可能几台主机
 * 相关的所有信息。大多数字段通过在逗号处分割
 * 相关的连接参数（例如pghost）得出。
 */
typedef struct pg_conn_host
{
	pg_conn_host_type type;		/* 主机地址类型 */
	char	   *host;			/* 主机名称或套接字路径 */
	char	   *hostaddr;		/* 主机数字IP地址 */
	char	   *port;			/* 端口号（始终提供） */
	char	   *password;		/* 此主机的密码，来自
								 * 密码文件；如果未寻求或在密码文件中未找到则为NULL。 */
} pg_conn_host;

/*
 * PGconn存储与与后端的单个连接相关的所有状态数据。
 */
struct pg_conn
{
	/* 保存的连接选项值 */
	char	   *pghost;			/* 服务器运行的机器，
								 * 或UNIX域套接字的路径，或一
								 * 个逗号分隔的机器和/或
								 * 路径列表；如果为NULL，则使用DEFAULT_PGSOCKET_DIR */
	char	   *pghostaddr;		/* 服务器运行的机器的数字IP地址，或同
								 * 一逗号分隔的列表。优先于pghost。 */
	char	   *pgport;			/* 服务器的通信端口号，或
								 * 逗号分隔的端口列表 */
	char	   *connect_timeout;	/* 连接超时（数字字符串） */
	char	   *pgtcp_user_timeout; /* TCP用户超时（数字字符串） */
	char	   *client_encoding_initial;	/* 要使用的编码 */
	char	   *pgoptions;		/* 启动后端的选项 */
	char	   *appname;		/* 应用程序名称 */
	char	   *fbappname;		/* 备用应用程序名称 */
	char	   *dbName;			/* 数据库名称 */
	char	   *replication;	/* 作为复制备份连接？ */
	char	   *pguser;			/* Postgres用户名和密码（如果有） */
	char	   *pgpass;
	char	   *pgpassfile;		/* 包含密码的文件路径 */
	char	   *channel_binding;	/* 通道绑定模式
									 * （require，prefer，disable） */
	char	   *keepalives;		/* 使用TCP keepalives？ */
	char	   *keepalives_idle;	/* TCP keepalives之间的时间 */
	char	   *keepalives_interval;	/* TCP keepalive
										 * 重传之间的时间 */
	char	   *keepalives_count;	/* TCP keepalive的最大
									 * 重传次数 */
	char	   *sslmode;		/* SSL模式（require，prefer，allow，disable） */
	char	   *sslcompression; /* SSL压缩（0或1） */
	char	   *sslkey;			/* 客户端密钥文件名 */
	char	   *sslcert;		/* 客户端证书文件名 */
#ifdef FDDGM
	char	   *sslenckey;		/* 加密客户端私钥文件名，TLCP */
	char	   *sslenccert;		/* 加密客户端证书文件名，TLCP */
#endif
	char	   *sslpassword;	/* 客户端密钥文件密码 */
	char	   *sslrootcert;	/* 根证书文件名 */
	char	   *sslcrl;			/* 证书撤销列表文件名 */
	char	   *sslcrldir;		/* 证书撤销列表目录名 */
	char	   *sslsni;			/* 是否使用SSL SNI扩展（0或1） */
	char	   *requirepeer;	/* 本地套接字所需的对等证书 */
	char	   *gssencmode;		/* GSS模式（要求、偏好、禁用） */
	char	   *krbsrvname;		/* Kerberos服务名称 */
	char	   *gsslib;			/* 使用哪个GSS库（"gssapi"或"sspi"） */
	char	   *ssl_min_protocol_version;	/* 最小TLS协议版本 */
	char	   *ssl_max_protocol_version;	/* 最大TLS协议版本 */
	char	   *target_session_attrs;	/* 所需的会话属性 */

	/* 可选文件，用于写入跟踪信息 */
	FILE	   *Pfdebug;
	int			traceFlags;

	/* 用于通知消息处理的回调过程 */
	PGNoticeHooks noticeHooks;

	/* 通过PQregisterEventProc注册的事件处理程序 */
	PGEvent    *events;			/* 可扩展的事件数据数组 */
	int			nEvents;		/* 活动事件的数量 */
	int			eventArraySize; /* 分配的数组大小 */

	/* 状态指示器 */
	ConnStatusType status;
	PGAsyncStatusType asyncStatus;
	PGTransactionStatusType xactStatus; /* 永远不会更改为ACTIVE */
	char		last_sqlstate[6];	/* 最后报告的SQLSTATE */
	bool		options_valid;	/* 如果可以尝试连接则为真 */
	bool		nonblocking;	/* 该连接是否使用非阻塞发送语义 */
	PGpipelineStatus pipelineStatus;	/* 管道模式的状态 */
	bool		singleRowMode;	/* 是否按行返回当前查询结果？ */
	char		copy_is_binary; /* 1 = 复制二进制，0 = 复制文本 */
	int			copy_already_done;	/* 在COPY OUT中已经返回的字节数 */
	PGnotify   *notifyHead;		/* 最旧的未报告的通知消息 */
	PGnotify   *notifyTail;		/* 最新的未报告的通知消息 */

	/* 连接字符串中对多个主机的支持 */
	int			nconnhost;		/* 在连接字符串中命名的主机数量 */
	int			whichhost;		/* 我们当前尝试/连接的主机 */
	pg_conn_host *connhost;		/* 关于每个命名主机的详细信息 */
	char	   *connip;			/* 当前网络连接的IP地址 */

	/*
	 * 作为单链表的待处理命令队列。头部是当前正在执行的命令，尾部是添加新命令的位置。
	 */
	PGcmdQueueEntry *cmd_queue_head;
	PGcmdQueueEntry *cmd_queue_tail;

	/*
	 * 为了节省malloc流量，我们不会立即释放条目；相反，我们将它们保存在这个列表中以备可能的重用。
	 */
	PGcmdQueueEntry *cmd_queue_recycle;

	/* 连接数据 */
	pgsocket	sock;			/* 套接字的FD，如果未连接，则为PGINVALID_SOCKET */
	SockAddr	laddr;			/* 本地地址 */
	SockAddr	raddr;			/* 远程地址 */
	ProtocolVersion pversion;	/* 正在使用的FE/BE协议版本 */
	int			sversion;		/* 服务器版本，例如70401表示7.4.1 */
	bool		auth_req_received;	/* 如果收到任何类型的认证请求则为真 */
	bool		password_needed;	/* 如果服务器要求密码则为真 */
	bool		sigpipe_so;		/* 我们是否通过SO_NOSIGPIPE屏蔽了SIGPIPE？ */
	bool		sigpipe_flag;	/* 我们是否可以通过MSG_NOSIGNAL屏蔽SIGPIPE？ */
	bool		write_failed;	/* 我们在套接字上是否发生了写入失败？ */
	char	   *write_err_msg;	/* 写入错误消息，如果内存不足则为NULL */

	/* 在建立连接时所需的瞬态状态 */
	PGTargetServerType target_server_type;	/* 所需的会话属性 */
	bool		try_next_addr;	/* 是否到达下一个地址/主机的时间？ */
	bool		try_next_host;	/* 是否到达下一个connhost[]的时间？ */
	struct addrinfo *addrlist;	/* 当前connhost的地址列表 */
	struct addrinfo *addr_cur;	/* 当前正在尝试的一个 */
	int			addrlist_family;	/* 需要知道如何释放addrlist */
	bool		send_appname;	/* 可以发送application_name吗？ */

	/* 杂项内容 */
	int			be_pid;			/* 后端的PID --- 需要用于取消 */
	int			be_key;			/* 后端的密钥 --- 需要用于取消 */
	pgParameterStatus *pstatus; /* ParameterStatus数据 */
	int			client_encoding;	/* 编码 ID */
	bool		std_strings;	/* 标准符合字符串 */
	PGTernaryBool default_transaction_read_only;	/* 默认事务只读 */
	PGTernaryBool in_hot_standby;	/* 热备份 */
	PGVerbosity verbosity;		/* 错误/通知消息的详细程度 */
	PGContextVisibility show_context;	/* 是否显示上下文字段 */
	PGlobjfuncs *lobjfuncs;		/* 访问大型对象功能的私有状态 */

	/* 从后端接收且尚未处理的数据缓冲区 */
	char	   *inBuffer;		/* 当前分配的缓冲区 */
	int			inBufSize;		/* 缓冲区的分配大小 */
	int			inStart;		/* 缓冲区中第一个未消费数据的偏移量 */
	int			inCursor;		/* 下一个待消费的字节 */
	int			inEnd;			/* 可用数据之后的第一个位置的偏移量 */

	/* 尚未发送到后端的数据缓冲区 */
	char	   *outBuffer;		/* 当前分配的缓冲区 */
	int			outBufSize;		/* 缓冲区的分配大小 */
	int			outCount;		/* 缓冲区中等待的字符数量 */

	/* 用于在输出缓冲区中构造消息的状态 */
	int			outMsgStart;	/* 消息起始的偏移量（长度字）；如果为-1，表示消息没有长度字 */
	int			outMsgEnd;		/* 消息结束的偏移量（到目前为止） */

	/* 行处理器接口工作空间 */
	PGdataValue *rowBuf;		/* 用于传递值给行处理器的数组 */
	int			rowBufLen;		/* 在 rowBuf 中分配的条目数量 */

	/*
	 * 异步结果构造的状态。如果 result 不是 NULL，那么它
	 * 是正在构造或准备返回的结果。如果 result 是 NULL
	 * 并且 error_result 为 true，那么我们需要返回一个 PGRES_FATAL_ERROR
	 * 结果，但还没有构造它；错误的文本已经附加到 conn->errorMessage 中。
	 * （延迟构造简化了处理内存不足情况的复杂性。）如果 next_result 不是 NULL，它是一个
	 * PGresult，在我们返回该结果之后将替换 "result"。
	 */
	PGresult   *result;			/* 正在构造的结果 */
	bool		error_result;	/* 我们需要生成一个错误结果吗？ */
	PGresult   *next_result;	/* 下一个结果（在单行模式下使用） */

	/* 各种 SASL、SSL、GSS 等的状态 */
	const pg_fe_sasl_mech *sasl;
	void	   *sasl_state;

	/* SSL 结构体 */
	bool		ssl_in_use;

#ifdef USE_SSL
	bool		allow_ssl_try;	/* 允许尝试 SSL 协商 */
	bool		wait_ssl_try;	/* 延迟 SSL 协商，直到尝试正常连接之后 */
#ifdef USE_OPENSSL
	SSL		   *ssl;			/* SSL 状态，如果有 SSL 连接 */
	X509	   *peer;			/* 服务器的 X509 证书 */
#ifdef USE_SSL_ENGINE
	ENGINE	   *engine;			/* SSL 引擎，如果有的话 */
#else
	void	   *engine;			/* 虚拟字段，以保持结构不变如果
	 * OpenSSL 版本发生变化 */
#endif
	bool		crypto_loaded;	/* 跟踪 libcrypto 锁定回调是否已
	 * 针对此连接完成。此项可以在移除对 OpenSSL 1.0.2 的支持
	 * 后删除，因为从 OpenSSL >= 1.1.0 开始，这种锁定将由内部处理。 */
#endif							/* USE_OPENSSL */
#endif							/* USE_SSL */

#ifdef ENABLE_GSS
	gss_ctx_id_t gctx;			/* GSS 上下文 */
	gss_name_t	gtarg_nam;		/* GSS 目标名称 */

	/* 以下内容仅用于加密 */
	bool		try_gss;		/* GSS 尝试被允许 */
	bool		gssenc;			/* GSS 加密可用 */
	gss_cred_id_t gcred;		/* GSS 凭证临时存储。 */

	/* GSS 加密 I/O 状态 --- 请参见 fe-secure-gssapi.c */
	char	   *gss_SendBuffer; /* 等待发送的加密数据 */
	int			gss_SendLength; /* gss_SendBuffer 中可用数据的结束 */
	int			gss_SendNext;	/* 下一个要从 gss_SendBuffer 发送的字节索引 */
	int			gss_SendConsumed;	/* 已加密但尚未报告为发送的源字节数 */
	char	   *gss_RecvBuffer; /* 接收到的加密数据 */
	int			gss_RecvLength; /* gss_RecvBuffer 中可用数据的结束 */
	char	   *gss_ResultBuffer;	/* 在 gss_RecvBuffer 中的数据解密 */
	int			gss_ResultLength;	/* End of data available in
									 * gss_ResultBuffer */
	int			gss_ResultNext; /* 下一索引从 gss_ResultBuffer 读取一个字节 */
	uint32		gss_MaxPktSize; /* 我们可以加密并将结果适配到输出缓冲区的最大大小 */
#endif

#ifdef ENABLE_SSPI
	CredHandle *sspicred;		/* SSPI 凭据句柄 */
	CtxtHandle *sspictx;		/* SSPI 上下文 */
	char	   *sspitarget;		/* SSPI 目标名称 */
	int			usesspi;		/* 指示是否在连接上使用 SSPI */
#endif

	/*
	 * 当前错误消息的缓冲区。此缓冲区在任何连接尝试或查询周期开始时被清除；之后，所有代码应该将消息追加到其中，而不是覆盖。
	 *
	 * 在某些情况下，我们可能在查询周期中多次报告错误。如果是这样，errorMessage 将累积所有错误的文本，而 errorReported 跟踪我们已经报告了多少，这样单个错误的 PGresult 对象就不会包含重复的文本。
	 */
	PQExpBufferData errorMessage;	/* 可扩展字符串 */
	int			errorReported;	/* 已报告字符串的字节数 */

	/* 用于接收消息各个部分的缓冲区 */
	PQExpBufferData workBuffer; /* 可扩展字符串 */
};

/* PGcancel 存储取消连接所需的所有数据。要安全地取消在不同线程上运行的连接，需要这部分数据的副本。 */
struct pg_cancel
{
	SockAddr	raddr;			/* 远程地址 */
	int			be_pid;			/* 后端的PID --- 需要用于取消 */
	int			be_key;			/* 后端的密钥 --- 需要用于取消 */
	int			pgtcp_user_timeout; /* tcp 用户超时 */
	int			keepalives;		/* 使用TCP keepalives？ */
	int			keepalives_idle;	/* TCP keepalives之间的时间 */
	int			keepalives_interval;	/* TCP keepalive
										 * 重传之间的时间 */
	int			keepalives_count;	/* TCP keepalive的最大
									 * 重传次数 */
};


/* ExecStatusTypes 的字符串描述。
 * 直接使用此数组已不推荐；请改用 PQresStatus()。
 */
extern char *const pgresStatus[];


#ifdef USE_SSL

#ifndef WIN32
#define USER_CERT_FILE		".postgresql/postgresql.crt"
#define USER_KEY_FILE		".postgresql/postgresql.key"
#ifdef FDDGM
#define USER_ENC_CERT_FILE		".postgresql/encpostgresql.crt" //加密证书
#define USER_ENC_KEY_FILE		".postgresql/encpostgresql.key" //加密证书私钥
#endif
#define ROOT_CERT_FILE		".postgresql/root.crt"
#define ROOT_CRL_FILE		".postgresql/root.crl"
#else
/* 在 Windows 上，“家”目录已经是 PostgreSQL 特定的 */
#define USER_CERT_FILE		"postgresql.crt"
#define USER_KEY_FILE		"postgresql.key"
#ifdef FDDGM
#define USER_ENC_CERT_FILE		"encpostgresql.crt" //加密证书
#define USER_ENC_KEY_FILE		"encpostgresql.key" //加密证书私钥
#endif
#define ROOT_CERT_FILE		"root.crt"
#define ROOT_CRL_FILE		"root.crl"
#endif

#endif							/* USE_SSL */

/* ----------------
 * libpq 的内部函数
 * 在此声明的函数需要在 libpq 的多个文件中可见，
 * 但并不打算被应用程序调用。我们使用
 * “pqXXX”作为内部函数的命名约定，而使用“PQxxx”
 * 用于应用程序可见的例程。
 * ----------------
 */

/* === 在 fe-connect.c 中 === */

extern void pqDropConnection(PGconn *conn, bool flushInput);
extern int	pqPacketSend(PGconn *conn, char pack_type,
						 const void *buf, size_t buf_len);
extern bool pqGetHomeDirectory(char *buf, int bufsize);

#ifdef ENABLE_THREAD_SAFETY
extern pgthreadlock_t pg_g_threadlock;

#define pglock_thread()		pg_g_threadlock(true)
#define pgunlock_thread()	pg_g_threadlock(false)
#else
#define pglock_thread()		((void) 0)
#define pgunlock_thread()	((void) 0)
#endif

/* === 在 fe-exec.c 中 === */

extern void pqSetResultError(PGresult *res, PQExpBuffer errorMessage, int offset);
extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
extern char *pqResultStrdup(PGresult *res, const char *str);
extern void pqClearAsyncResult(PGconn *conn);
extern void pqSaveErrorResult(PGconn *conn);
extern PGresult *pqPrepareAsyncResult(PGconn *conn);
extern void pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) pg_attribute_printf(2, 3);
extern void pqSaveMessageField(PGresult *res, char code,
							   const char *value);
extern void pqSaveParameterStatus(PGconn *conn, const char *name,
								  const char *value);
extern int	pqRowProcessor(PGconn *conn, const char **errmsgp);
extern void pqCommandQueueAdvance(PGconn *conn, bool isReadyForQuery,
								  bool gotSync);
extern int	PQsendQueryContinue(PGconn *conn, const char *query);

/* === 在 fe-protocol3.c 中 === */

extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
								   const PQEnvironmentOption *options);
extern void pqParseInput3(PGconn *conn);
extern int	pqGetErrorNotice3(PGconn *conn, bool isError);
extern void pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
								 PGVerbosity verbosity, PGContextVisibility show_context);
extern int	pqGetCopyData3(PGconn *conn, char **buffer, int async);
extern int	pqGetline3(PGconn *conn, char *s, int maxlen);
extern int	pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
extern int	pqEndcopy3(PGconn *conn);
extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
								 int *result_buf, int *actual_result_len,
								 int result_is_int,
								 const PQArgBlock *args, int nargs);

/* === 在 fe-misc.c 中 === */

 /*
  * “获取”和“放置”例程成功时返回 0，失败时返回 EOF。请注意，对于
  * 获取，EOF 仅表示缓冲区已耗尽，并不一定意味着有
  * 错误。
  */
extern int	pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn);
extern int	pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn);
extern int	pqGetc(char *result, PGconn *conn);
extern int	pqPutc(char c, PGconn *conn);
extern int	pqGets(PQExpBuffer buf, PGconn *conn);
extern int	pqGets_append(PQExpBuffer buf, PGconn *conn);
extern int	pqPuts(const char *s, PGconn *conn);
extern int	pqGetnchar(char *s, size_t len, PGconn *conn);
extern int	pqSkipnchar(size_t len, PGconn *conn);
extern int	pqPutnchar(const char *s, size_t len, PGconn *conn);
extern int	pqGetInt(int *result, size_t bytes, PGconn *conn);
extern int	pqPutInt(int value, size_t bytes, PGconn *conn);
extern int	pqPutMsgStart(char msg_type, PGconn *conn);
extern int	pqPutMsgEnd(PGconn *conn);
extern int	pqReadData(PGconn *conn);
extern int	pqFlush(PGconn *conn);
extern int	pqWait(int forRead, int forWrite, PGconn *conn);
extern int	pqWaitTimed(int forRead, int forWrite, PGconn *conn,
						time_t finish_time);
extern int	pqReadReady(PGconn *conn);
extern int	pqWriteReady(PGconn *conn);

/* === 在 fe-secure.c 中 === */

extern int	pqsecure_initialize(PGconn *, bool, bool);
extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
extern void pqsecure_close(PGconn *);
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);

#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
							 bool got_epipe);
#endif

/* === SSL === */

/*
 * SSL 实现提供这些函数。
 */

/*
 * PQinitSSL() 的实现。
 */
extern void pgtls_init_library(bool do_ssl, int do_crypto);

/*
 * 初始化 SSL 库。
 *
 * conn 参数仅用于能够传回错误
 * 消息 - 这里不进行任何连接本地的设置。do_ssl 控制
 * SSL 是否被初始化，do_crypto 对加密部分做同样的操作。
 *
 * 返回 0 表示成功，-1 表示失败（将消息添加到 conn->errorMessage）。
 */
extern int	pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto);

/*
 * 开始或继续协商安全会话。
 */
extern PostgresPollingStatusType pgtls_open_client(PGconn *conn);

/*
 *	关闭 SSL 连接。
 */
extern void pgtls_close(PGconn *conn);

/*
 *	从安全连接读取数据。
 *
 * 在失败时，此函数负责将适当的消息附加到 conn->errorMessage。调用者仍须检查 errno，但仅仅是为了确定在错误后是否继续/重试。
 */
extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);

/*
 *	SSL 读取缓冲区中是否有未读数据？
 */
extern bool pgtls_read_pending(PGconn *conn);

/*
 *	向安全连接写入数据。
 *
 * 在失败时，此函数负责将适当的消息附加到 conn->errorMessage。调用者仍须检查 errno，但仅仅是为了确定在错误后是否继续/重试。
 */
extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);

/*
 * 获取服务器证书的哈希值，用于 SCRAM 通道绑定类型
 * tls-server-end-point。
 *
 * 如果发生错误，将返回 NULL，并附带一个错误消息供调用者使用。
 *
 * 这在旧版本的 OpenSSL 中不被支持，因为它们没有
 * X509_get_signature_nid() 函数。
 */
#if defined(USE_OPENSSL) && (defined(HAVE_X509_GET_SIGNATURE_NID) || defined(HAVE_X509_GET_SIGNATURE_INFO))
#define HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
extern char *pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len);
#endif

/*
 * 验证服务器证书是否与我们连接的主机名匹配。
 *
 * 证书的公用名和主题备用名称会被考虑。
 *
 * 名称匹配时返回 1，不匹配时返回 0。发生错误时，返回
 * -1，并设置 libpq 错误消息。
 *
 */
extern int	pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
															int *names_examined,
															char **first_name);

/* === GSSAPI === */

#ifdef ENABLE_GSS

/*
 * 建立一个 GSSAPI 加密连接。
 */
extern PostgresPollingStatusType pqsecure_open_gss(PGconn *conn);

/*
 * GSSAPI 加密连接的读写函数，具有内部缓冲，以处理非阻塞套接字。
 */
extern ssize_t pg_GSS_write(PGconn *conn, const void *ptr, size_t len);
extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
#endif

/* === 在 libpq-trace.c 中 === */

extern void pqTraceOutputMessage(PGconn *conn, const char *message,
								 bool toServer);
extern void pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message);

/* === 其他宏 === */

/*
 * 重置 conn 的错误报告状态。
 */
#define pqClearConnErrorState(conn) \
	(resetPQExpBuffer(&(conn)->errorMessage), \
	 (conn)->errorReported = 0)

/*
 * 检查我们是否有待返回的 PGresult --- 无论是 conn->result 中构造的，还是我们不打算在查询周期结束之前物化的“虚拟”错误结果。
 */
#define pgHavePendingResult(conn) \
	((conn)->result != NULL || (conn)->error_result)

/*
 * 这样我们就可以在内部检查连接是否是非阻塞的，而没有函数调用的开销。
 */
#define pqIsnonblocking(conn)	((conn)->nonblocking)

/*
 * 连接的输出缓冲区阈值，用于管道模式。
 */
#define OUTBUFFER_THRESHOLD	65536

#ifdef ENABLE_NLS
extern char *libpq_gettext(const char *msgid) pg_attribute_format_arg(1);
extern char *libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) pg_attribute_format_arg(1) pg_attribute_format_arg(2);
#else
#define libpq_gettext(x) (x)
#define libpq_ngettext(s, p, n) ((n) == 1 ? (s) : (p))
#endif
/*
 * libpq 代码应使用上述内容，而不是 _()，因为那会使用
 * 周围程序的消息目录。
 */
#undef _

/*
 * 这些宏是为了让错误处理代码在 Unix 和 Windows 之间可移植。(呃)
 */
#ifdef WIN32
#define SOCK_ERRNO (WSAGetLastError())
#define SOCK_STRERROR winsock_strerror
#define SOCK_ERRNO_SET(e) WSASetLastError(e)
#else
#define SOCK_ERRNO errno
#define SOCK_STRERROR strerror_r
#define SOCK_ERRNO_SET(e) (errno = (e))
#endif

#endif							/* LIBPQ_INT_H */
